package cubelabs.nexus.service;

import java.io.DataInputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.brickred.socialauth.EducationHistory;
import org.brickred.socialauth.Profile;
import org.brickred.socialauth.WorkHistory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.opentok.api.OpenTokSDK;
import com.opentok.api.constants.RoleConstants;

import cubelabs.nexus.dao.AvailabilityDAO;
import cubelabs.nexus.dao.CategoryDAO;
import cubelabs.nexus.dao.CounsellingSessionDAO;
import cubelabs.nexus.dao.CouponDAO;
import cubelabs.nexus.dao.EducationDAO;
import cubelabs.nexus.dao.EventDAO;
import cubelabs.nexus.dao.EventSubscriptionDAO;
import cubelabs.nexus.dao.InvitationDAO;
import cubelabs.nexus.dao.MessageDAO;
import cubelabs.nexus.dao.OrganizationDAO;
import cubelabs.nexus.dao.SessionDAO;
import cubelabs.nexus.dao.UserDAO;
import cubelabs.nexus.dao.UserWorkDAO;
import cubelabs.nexus.dao.ViewerDAO;
import cubelabs.nexus.domain.CalEvent;
import cubelabs.nexus.domain.CalendarDTO;
import cubelabs.nexus.domain.CounselEventCreationRequest;
import cubelabs.nexus.domain.CounselRequestDTO;
import cubelabs.nexus.domain.CounsellingEventReview;
import cubelabs.nexus.domain.CouponDTO;
import cubelabs.nexus.domain.EducationDTO;
import cubelabs.nexus.domain.FileUploadResponseDTO;
import cubelabs.nexus.domain.InvitationDTO;
import cubelabs.nexus.domain.MessageDTO;
import cubelabs.nexus.domain.NexusFileUpload;
import cubelabs.nexus.domain.OneToMany;
import cubelabs.nexus.domain.SessionDTO;
import cubelabs.nexus.domain.SessionData;
import cubelabs.nexus.domain.UserDTO;
import cubelabs.nexus.domain.UserSearchDTO;
import cubelabs.nexus.domain.WorkDTO;
import cubelabs.nexus.entity.Availability;
import cubelabs.nexus.entity.Category;
import cubelabs.nexus.entity.CounsellingSession;
import cubelabs.nexus.entity.Coupon;
import cubelabs.nexus.entity.Education;
import cubelabs.nexus.entity.Event;
import cubelabs.nexus.entity.EventSubscription;
import cubelabs.nexus.entity.Invitation;
import cubelabs.nexus.entity.Message;
import cubelabs.nexus.entity.Organization;
import cubelabs.nexus.entity.Session;
import cubelabs.nexus.entity.User;
import cubelabs.nexus.entity.UserWork;
import cubelabs.nexus.entity.Viewer;
import cubelabs.nexus.enums.InvitationStatus;
import cubelabs.nexus.enums.MessageStatus;
import cubelabs.nexus.enums.MessageType;
import cubelabs.nexus.enums.SessionStatus;
import cubelabs.nexus.utils.FileUploadUtils;
import cubelabs.nexus.utils.GenericUtils;
import cubelabs.nexus.utils.MailUtil;

@Service
public class NexusService {

	@Autowired
	private UserDAO userDAO;

	@Autowired
	private CategoryDAO categoryDAO;



	@Autowired
	private InvitationDAO invitationDAO;


	@Autowired
	private ViewerDAO viewerDAO;


	@Autowired
	private SessionDAO sessionDAO;


	@Autowired
	private EventDAO eventDAO;

	@Autowired
	private MessageDAO messageDAO;


	@Autowired
	private EventSubscriptionDAO eventSubscriptionDAO;

	@Autowired
	private CounsellingSessionDAO counsellingSessionDAO;

	@Autowired
	private CouponDAO couponDAO;


	@Autowired
	private UserWorkDAO userWorkDAO;


	@Autowired
	private EducationDAO educationDAO;

	@Autowired
	private AvailabilityDAO availabilityDAO;

	@Autowired
	private OrganizationDAO organizationDAO;

	@Transactional
	public User fetchUserById(Long userId){
		User user = null;
		if(userId != null){
			user = userDAO.findById(userId, false);
			//	user.getCategories();
		}
		return user;
	}

	@Transactional
	public Category fetchCategoryByName(String categoryName){
		Category category = null;
		if(categoryName != null){
			category = categoryDAO.findByName(categoryName);
		}
		return category;
	}



	@Transactional
	public List<UserSearchDTO> fetchUsersByCategoryId(Long categoryId, User sessionUser){
		List<User> users= categoryDAO.getUsers(categoryId);

		long sessionUserId = -1 ;

		if(sessionUser != null){
			sessionUserId = sessionUser.getId();
		}

		List<UserSearchDTO> userDTOList = null;
		if(users != null && users.size()> 0){
			userDTOList = new ArrayList<UserSearchDTO>();
			for(User user:users){
				UserSearchDTO usDTO = new UserSearchDTO();
				usDTO.setId(user.getId());
				usDTO.setEmail(user.getEmail());
				usDTO.setName(user.getName());
				usDTO.setImagePath(user.getImagePath());
				long rating = 0L;
				usDTO.setRating(rating);
				List<CounsellingEventReview> reviews = findReviewByUserId(user.getId());
				if(reviews != null && reviews.size() > 0){
					for(CounsellingEventReview r : reviews){
						if(r.getRating() != null) {
							rating += r.getRating();
						}
					}
					usDTO.setRating(rating/reviews.size());
				}


				if(sessionUserId != usDTO.getId())
					userDTOList.add(usDTO);
			}

		}


		return userDTOList;
	}

	/*
	 * fetchExperts returns the experts whose name matches/contains the search string
	 */
	@Transactional
	public List<UserSearchDTO> fetchExperts(String name){
		List<User> users= userDAO.findExpertByName(name);
		List<UserSearchDTO> userDTOList = null;

		if(users == null || (users != null && users.size() == 0)) {
			users= userDAO.findExpertLikeName(name);
		}

		if(users != null && users.size()> 0){
			userDTOList = new ArrayList<UserSearchDTO>();
			for(User user:users){
				UserSearchDTO usDTO = new UserSearchDTO();
				usDTO.setId(user.getId());
				usDTO.setEmail(user.getEmail());
				usDTO.setName(user.getName());
				usDTO.setImagePath(user.getImagePath());
				long rating = 0L;
				List<CounsellingEventReview> reviews = findReviewByUserId(user.getId());
				int size = 1;
				if(reviews != null && reviews.size() > 0){
					for(CounsellingEventReview r : reviews){
						rating += r.getRating();
					}

					size = reviews.size();
				}
				usDTO.setRating(rating/size);

				userDTOList.add(usDTO);
			}

		}
		return userDTOList;
	}

	/*
	 * fetchExpertTalks returns the expert talks - one 2 many events
	 */
	@Transactional
	public List<SessionDTO> fetchExpertTalks(){
		List<Session> sessions = sessionDAO.findExpertTalks();
		List<SessionDTO> sessionDTOs = new ArrayList<SessionDTO>();
		if(sessions != null) {
			for (Session session: sessions){
				SessionDTO sessionDTO = GenericUtils.convertSession(session);
				sessionDTO.setExpertDTO(GenericUtils.getUserDTO(session.getExpert()));
				sessionDTOs.add(sessionDTO);
			}
		}
		return sessionDTOs;
	}

	/*
	 * fetchExpertTalks returns the expert talks(one 2 many events) filtered by expert 
	 */
	@Transactional
	public List<Event> fetchExpertTalksByExpert(String name){

		List<Event>  eventLst = eventDAO.findExpertTalksByExpert(name);

		return eventLst;
	}

	@Transactional
	public List<UserSearchDTO> fetchAllUsers(){
		List<User> users= userDAO.findAll();
		List<UserSearchDTO> userDTOList = null;
		if(users != null && users.size()> 0){
			userDTOList = new ArrayList<UserSearchDTO>();
			for(User user:users){
				UserSearchDTO usDTO = new UserSearchDTO();
				usDTO.setId(user.getId());
				usDTO.setEmail(user.getEmail());
				usDTO.setName(user.getName());
				usDTO.setImagePath(user.getImagePath());
				long rating = 0L;
				List<CounsellingEventReview> reviews = findReviewByUserId(user.getId());
				int size = 1;
				if(reviews != null && reviews.size() > 0){
					for(CounsellingEventReview r : reviews){
						rating += r.getRating();
					}

					size = reviews.size();
				}
				usDTO.setRating(rating/size);

				userDTOList.add(usDTO);
			}

		}
		return userDTOList;
	}


	@Transactional
	public List<Category> findAllCategories() {
		return categoryDAO.findAll();
	}

	@Transactional
	public List<Category> findTrendingCategories() {
		return categoryDAO.findTrendingCategories();
	}


	@Transactional
	public List<CounsellingEventReview> findReviewByUserId(Long userId) {
		List<CounsellingEventReview> counsellingEventReviews = null;
		User user ;

		List<Session> sessions = sessionDAO.findByExpert(userId);
		if(sessions != null && sessions.size() > 0){
			counsellingEventReviews = new ArrayList<CounsellingEventReview>();
			for(Session session : sessions){
				for(Viewer viewer: session.getViewers()) {

					CounsellingEventReview counsellingEventReview = new CounsellingEventReview();
					//	counsellingEventReview.setDescription(viewer.getReview().get);
					if(viewer.getReview() != null) {
						counsellingEventReview.setRating(viewer.getReview().getAvgRating());
					}
					user = session.getExpert();
					UserSearchDTO userDTO = new UserSearchDTO();
					userDTO.setEmail(user.getEmail());
					userDTO.setName(user.getName());
					userDTO.setImagePath(user.getImagePath());
					userDTO.setId(user.getId());
					counsellingEventReview.setUser(userDTO);
					counsellingEventReviews.add(counsellingEventReview);
				}
			}

		}
		return counsellingEventReviews;
	}

	@Transactional
	public UserDTO findUserDetails(Long userId) {

		User user = fetchUserById(userId);

		UserDTO userDTO = new UserDTO();
		userDTO.setEmail(user.getEmail());
		userDTO.setName(user.getName());
		userDTO.setImagePath(user.getImagePath());
		userDTO.setId(user.getId());
		userDTO.setAboutme(user.getAboutme());
		userDTO.setExpert(user.isExpert());
		List<CounsellingEventReview> reviews = findReviewByUserId(userId);
		long rating = 0;
		int size = 1;
		if(reviews != null && reviews.size() > 0){
			for(CounsellingEventReview r : reviews){
				if(r.getRating() != null)
					rating += r.getRating();
			}

			size = reviews.size();
		}
		userDTO.setRating(rating/size);

		userDTO.setReviews(reviews);

		List<EducationDTO> eduHistory = null;
		List<Education> eduList = user.getEducationHistory();
		if(eduList != null && eduList.size() > 0){
			eduHistory = new ArrayList<EducationDTO>();
			for(Education e :  eduList){
				EducationDTO edu = new EducationDTO();
				edu.setCollegeName(e.getCollegename());
				edu.setEducationType(e.getType());
				edu.setYearOfCompletion(e.getYoc());
				edu.setId(e.getId());
				eduHistory.add(edu);
			}

		}
		userDTO.setEducationHistory(eduHistory);

		List<WorkDTO> workHistory = null;
		List<UserWork> workList = user.getWorkHistory();
		if(workList != null && workList.size() > 0){
			workHistory = new ArrayList<WorkDTO>();
			for(UserWork w : workList){
				WorkDTO work = new WorkDTO();
				work.setCompanyName(w.getCompanyname());
				work.setEndDate(w.getEnddate());
				work.setStartDate(w.getStartdate());
				work.setId(w.getId());

				workHistory.add(work);
			}

		}
		userDTO.setWorkHistory(workHistory);

		List<String> categories = new ArrayList<String>();

		for(Category category : user.getCategories()){
			categories.add(category.getName());
		}

		userDTO.setCategories(categories);

		return userDTO;

	}



	@Transactional
	public List<CounselRequestDTO> findCounselRequestsByUserId(Long userId) {
		List<CounselRequestDTO> counselRequestDTOList = new ArrayList<CounselRequestDTO>();

		List<Viewer> viewers = viewerDAO.findByUserId(userId);

		for(Viewer viewer: viewers) {
			Session counsellingEvent = viewer.getSession();
			CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
			counselRequestDTO.setDescription(counsellingEvent.getHtmlDescription());
			counselRequestDTO.setEndTime(counsellingEvent.getEndTime());
			counselRequestDTO.setStartTime(counsellingEvent.getStartTime());
			counselRequestDTO.setId(counsellingEvent.getId());
			counselRequestDTO.setSessionStatus(counsellingEvent.getSessionStatus());
			counselRequestDTO.setFrmuserDTO(GenericUtils.getUserDTO(counsellingEvent.getExpert()));
			counselRequestDTO.setExpert(false);
			counselRequestDTOList.add(counselRequestDTO);
		}
		List<Session> counsellingEvents = sessionDAO.findByExpert(userId);

		for(Session counsellingEvent : counsellingEvents){
			CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
			counselRequestDTO.setDescription(counsellingEvent.getHtmlDescription());
			counselRequestDTO.setEndTime(counsellingEvent.getEndTime());
			counselRequestDTO.setStartTime(counsellingEvent.getStartTime());
			counselRequestDTO.setId(counsellingEvent.getId());
			counselRequestDTO.setSessionStatus(counsellingEvent.getSessionStatus());
			counselRequestDTO.setExpert(true);
			counselRequestDTO.setFrmuserDTO(GenericUtils.getUserDTO(counsellingEvent.getExpert()));
			//counselRequestDTO.setToUserDTO(GenericUtils.getUserDTO(counsellingEvent.getToUser()));
			counselRequestDTOList.add(counselRequestDTO);
		}
		return counselRequestDTOList;
	}

	/*
	@Transactional
	public List<CounselRequestDTO> findCounselPendingRequestPerUserId(Long userId) {
		List<CounselRequestDTO> counselRequestDTOList = new ArrayList<CounselRequestDTO>();
		List<CounsellingEvent> counsellingEvents = counsellingEventDAO.findByPendingToUser(userId);
		for(CounsellingEvent counsellingEvent : counsellingEvents){
			CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
			counselRequestDTO.setDescription(counsellingEvent.getDiscription());
			counselRequestDTO.setEndTime(counsellingEvent.getEnd());
			counselRequestDTO.setStartTime(counsellingEvent.getStart());
			counselRequestDTO.setId(counsellingEvent.getId());
			counselRequestDTO.setFrmuserDTO(GenericUtils.getUserDTO(counsellingEvent.getFrmUser()));
			counselRequestDTO.setToUserDTO(GenericUtils.getUserDTO(counsellingEvent.getToUser()));
			counselRequestDTOList.add(counselRequestDTO);
		}
		return counselRequestDTOList;
	}
	 */

	@Transactional
	public List<InvitationDTO> findNotifications(Long userId) {
		List<InvitationDTO> invitationsDTO = new ArrayList<InvitationDTO>();		
		List<Invitation> invitations = invitationDAO.findToInvitionsByUserId(userId);
		for(Invitation invitation : invitations){
			invitationsDTO.add(GenericUtils.convertInvitation(invitation));
		}
		return invitationsDTO;
	}


	@Transactional
	public User fetchUserByEmail(String email) {
		User user = null;
		if(email != null){
			user = userDAO.findByEmail(email);
		}
		return user;	
	}

	@Transactional
	public User createUser(Profile userDTO) {
		User user = copyUserToUserDTO(userDTO);
		user.setMyFavoriteUsers(new HashSet<User>());
		user.setFavoriteOrgs(new HashSet<Organization>());
		user = userDAO.makePersistent(user);
		sendMail(user, null, "signUp", null);


		//Creating notifications for a new user
		Message message = new Message();
		message.setMessageContent("Welcome to Meet expert. " +
				"We will let you interact, share ideas and seek help from the best. Whenever you want. " +
				"Where ever you want.");
		message.setToId(user.getId());
		message.setMessageType(MessageType.INFO);
		message.setFromId(1L);//TODO
		message.setMessageStatus(MessageStatus.CREATED);
		messageDAO.makePersistent(message);


		return user;
	}

	public User copyUserToUserDTO(Profile profile){
		User user = new User();
		user.setEmail(profile.getEmail());
		user.setImagePath(profile.getProfileImageURL()!=null?profile.getProfileImageURL()+"?width=150&height=150":"http://web-2.oauife.edu.ng/economics/images/no_photo.jpg");
		user.setName(profile.getFullName());
		user.setActive(true);
		user.setLastLogin(new Date());
		user.setAboutme(profile.getAboutme());
		if(profile.getWork() != null && profile.getWork().size() > 0){
			ArrayList<UserWork> workList = new ArrayList<UserWork>();
			for(WorkHistory work : profile.getWork()){
				UserWork w = new UserWork();
				w.setCompanyname(work.getCompanyName());
				w.setStartdate(work.getStartDate());;
				w.setEnddate(work.getEndDate());
				w.setUser(user);
				workList.add(w);
			}
			user.setWorkHistory(workList);
		}

		if(profile.getEducation() != null && profile.getEducation().size() > 0){
			ArrayList<Education> eduList = new ArrayList<Education>();
			for(EducationHistory edu : profile.getEducation()){
				Education e = new Education();
				e.setCollegename(edu.getCollegeName());
				e.setType(edu.getEducationType());;
				e.setYoc(edu.getYearOfCompletion());
				e.setUser(user);
				eduList.add(e);
			}
			user.setEducationHistory(eduList);
		}

		Set<Category> categories = new HashSet<Category>();
		if(profile.getSkills() != null && profile.getSkills().size() > 0){
			for(String s : profile.getSkills()){
				Category c = categoryDAO.findByName(s);
				if(c != null){
					categories.add(c);
				}else{
					c = new Category();
					c.setName(s);
					c = categoryDAO.makePersistent(c);
					categories.add(c);
				}
			}
		}
		user.setCategories(categories);

		return user;
	}



	@Transactional
	public void copyUserDTOToUser(UserDTO profile){
		User user = userDAO.findById(profile.getId(), true);
		user.setName(profile.getName());
		user.setActive(true);
		user.setLastLogin(new Date());
		user.setAboutme(profile.getAboutme());
		Map<Long,UserWork>  workMap=user.getWorkMap(user.getWorkHistory());
		List<UserWork> workList = new ArrayList<UserWork>();
		if(workMap != null && workMap.size() > 0){
			for(WorkDTO work : profile.getWorkHistory()){
				UserWork w = null;
				if(work.getId() == null) {
					w = new UserWork();
				} else {
					w = userWorkDAO.findById(work.getId(), false);
				}

				w.setCompanyname(work.getCompanyName());
				w.setStartdate(work.getStartDate());;
				w.setEnddate(work.getEndDate());
				w.setId(work.getId());
				workList.add(w);
				//		w.setUser(user);
			}

		}
		user.setWorkHistory(workList);
		List<Education> eduList = new ArrayList<Education>();
		@SuppressWarnings("unused")
		Map<Long,Education> eduMap=user.getEducationMap(user.getEducationHistory());
		if(profile.getEducationHistory() != null && profile.getEducationHistory().size() > 0){
			for(EducationDTO edu : profile.getEducationHistory()){
				Education e = null;
				if(edu.getId() == null){
					e = new Education();
				} else {
					e = educationDAO.findById(edu.getId(), false);
				}
				e.setCollegename(edu.getCollegeName());
				e.setType(edu.getEducationType());;
				e.setYoc(edu.getYearOfCompletion());
				e.setId(edu.getId());
				eduList.add(e);
				//		e.setUser(user);
			}
		}
		user.setEducationHistory(eduList);

		Set<Category> categories = new HashSet<Category>();
		for(String category: profile.getCategories()){
			Category c = categoryDAO.findByName(category);
			if(c != null){
				categories.add(c);
			}else{
				c = new Category();
				c.setName(category);
				c = categoryDAO.makePersistent(c);

				categories.add(c);
			}
		}
		user.setCategories(categories);
		user.setExpert(profile.isExpert());
		userDAO.merge(user);
		userDAO.flush();

	}


	@Transactional
	public void createSession(Session session) {
		session = sessionDAO.makePersistent(session);
		if(session.getExpertInvitation() != null) {
			Invitation invitation = session.getExpertInvitation();
			MessageDTO messageDTO = new MessageDTO();
			if(invitation.getInviter() == null) {
				messageDTO.setFromEmail(invitation.getInviterEmail());
			} else {
				messageDTO.setFromUser(GenericUtils.getUserDTO(invitation.getInviter()));
			}

			CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
			counselRequestDTO.setId(session.getId());
			messageDTO.setCounselRequestDTO(counselRequestDTO);

			messageDTO.setToUser(GenericUtils.getUserDTO(invitation.getInvitee()));
			messageDTO.setMessage(session.getTitle());
			messageDTO.setMessageType(MessageType.SESSIONREQUEST);
			messageDTO.setMessageStatus(MessageStatus.CREATED);
			invitation = invitationDAO.makePersistent(invitation);
			messageDTO.setInvitationId(invitation.getId());
			createOrUpdateMessage(messageDTO);
		}

		if(session.getInvitation() != null ) {
			for(Invitation invitation: session.getInvitation()) {
				if(invitation.getInvitee() == null) {
					//send Email;

				} else {
					if(invitation.getInvitee().getId() == session.getExpert().getId()) {

					} else {

					}
					MessageDTO messageDTO = new MessageDTO();
					if(invitation.getInviter() == null) {
						messageDTO.setFromEmail(invitation.getInviterEmail());
					} else {
						messageDTO.setFromUser(GenericUtils.getUserDTO(invitation.getInviter()));
					}

					CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
					counselRequestDTO.setId(session.getId());
					messageDTO.setCounselRequestDTO(counselRequestDTO);

					messageDTO.setToUser(GenericUtils.getUserDTO(invitation.getInvitee()));
					messageDTO.setMessage(session.getTitle());
					messageDTO.setMessageType(MessageType.SESSIONREQUEST);
					messageDTO.setMessageStatus(MessageStatus.CREATED);

					invitation = invitationDAO.makePersistent(invitation);
					messageDTO.setInvitationId(invitation.getId());
					createOrUpdateMessage(messageDTO);
				}

			}
		}
		if(session.getViewers() != null) {
			for(Viewer viewer: session.getViewers()){
				viewerDAO.makePersistent(viewer);
			}
		}
		invitationDAO.makePersistent(session.getExpertInvitation());

		session = sessionDAO.makePersistent(session);

	}

	/*
	@Transactional
	public void createEvent(Session session) {
		//counsellingAcceptorDAO.makePersistent(counsellingEvent.getCounsellingAcceptor());
		counsellingEvent = counsellingEventDAO.makePersistent(counsellingEvent);
		//to send notification at the time of scheduling session
		sendMail(counsellingEvent.getToUser(), counsellingEvent.getFrmUser(), "toCounseler", counsellingEvent.getStart());
		sendMail(counsellingEvent.getToUser(), counsellingEvent.getFrmUser(), "toCounselee", counsellingEvent.getStart());

		MessageDTO messageDTO = new MessageDTO();
		messageDTO.setFromUser(GenericUtils.getUserDTO(counsellingEvent.getFrmUser()));
		messageDTO.setToUser(GenericUtils.getUserDTO(counsellingEvent.getToUser()));
		messageDTO.setMessage(counsellingEvent.getDiscription());
		messageDTO.setMessageType(MessageType.SESSIONREQUEST);
		messageDTO.setMessageStatus(MessageStatus.CREATED);

		CounselRequestDTO counselRequestDTO = new CounselRequestDTO();
		counselRequestDTO.setDescription(counsellingEvent.getDiscription());
		counselRequestDTO.setEndTime(counsellingEvent.getEnd());
		counselRequestDTO.setStartTime(counsellingEvent.getStart());
		counselRequestDTO.setId(counsellingEvent.getId());
		counselRequestDTO.setFrmuserDTO(GenericUtils.getUserDTO(counsellingEvent.getFrmUser()));
		counselRequestDTO.setToUserDTO(GenericUtils.getUserDTO(counsellingEvent.getToUser()));

		messageDTO.setCounselRequestDTO(counselRequestDTO);
		createOrUpdateMessage(messageDTO);
		//counsellingEventDAO.makePersistent(counsellingEvent);
	}
	 */

	@Transactional
	public CounsellingSession saveCounsellingSession(Long councellingSessionId) {
		CounsellingSession counsellingSession = new CounsellingSession();

		OpenTokSDK openTokSDK = new OpenTokSDK(21313161, "2c819ea062cc6557e618a4de655bcdce9e33d4f4");
		try {
			String sessionId = openTokSDK.create_session().session_id;
			String token = openTokSDK.generate_token(sessionId);

			counsellingSession.setSessionId(sessionId);
			counsellingSession.setToken(token);

			counsellingSession.setSession(sessionDAO.findById(councellingSessionId,false));

			counsellingSession.setApiKey(21313161);
			counsellingSession.setModeratorToken(openTokSDK.generate_token(sessionId, RoleConstants.MODERATOR));
			counsellingSession.setSubscriberToken(openTokSDK.generate_token(sessionId, RoleConstants.SUBSCRIBER));
			counsellingSession.setPublisherToken(openTokSDK.generate_token(sessionId, RoleConstants.PUBLISHER));
		}catch(Exception exception){
			exception.printStackTrace();
		}
		return counsellingSessionDAO.makePersistent(counsellingSession);
	}

	@Transactional
	public SessionData fetchSession(Long counselEventId, Long userId) {
		
		CounsellingSession counsellingSession = counsellingSessionDAO.findBySession(counselEventId);

		if(counsellingSession != null){
			
			Session session = counsellingSession.getSession();
			
			SessionData sessionData = new SessionData();
			sessionData.setApiKey(21313161);
			sessionData.setModeratorToken(counsellingSession.getModeratorToken());
			sessionData.setPublisherToken(counsellingSession.getPublisherToken());
			sessionData.setSessionId(counsellingSession.getSessionId());
			sessionData.setSubscriberToken(counsellingSession.getSubscriberToken());
			sessionData.setToken(counsellingSession.getToken());
			sessionData.setOneToMany(session.isOneToMany());
			
			if(session.getExpert().getId().equals(userId)){
				sessionData.setExpert(true);
			}
			return sessionData;
		}else
			return null;
	}

	/*
	@Transactional
	@Scheduled(fixedRate=1000*900)
	public void sendnotifications() {
		System.out.println(" running scheduler ");
		List<Session> sessions = sessionDAO.findAboutToStartSession(2l);
		System.out.println("sessions size:" + sessions.size());
		if(sessions != null & sessions.size() > 0 ){
			for(Session session :sessions){

				//sendMail(session., counsellingEvent.getFrmUser(), "toCounselerReminder", counsellingEvent.getStart());
				//sendMail(counsellingEvent.getToUser(), counsellingEvent.getFrmUser(), "toCounseleeReminder", counsellingEvent.getStart());

			}
		} else {
			System.out.println(" No sessions in next 60 seconds ");
		}

	}
	 */
	@SuppressWarnings("deprecation")
	private  Map<String, String> sendMail(User toUser, User fromUser, String event, Date start) {

		System.out.println("Sending email to "+toUser.getEmail());

		Map<String, Object> inputMap = new HashMap<String, Object>();



		Map<String, Object> valueMap = new HashMap<String, Object>();
		String filename = new String();

		if(event.equalsIgnoreCase("toCounseler")) {
			inputMap.put("subject", "Meet Expert session");		
			inputMap.put("to", toUser.getEmail());
			valueMap.put("#COUNSELER_NAME#", toUser.getName());
			valueMap.put("#COUNSELE_NAME#", fromUser.getName());
			valueMap.put("#DATE#", start.toString());
			valueMap.put("#CUSTOM_MESSAGE#", " ");

			filename = "ToCounseler.txt";
			//inputMap.put("text", "Hi "+toUser.getName()+", You have a new session request from "+ fromUser.getName() +" on Meet Expert for "+proposedStartTime+". -Meet Expert.");
		} else if(event.equalsIgnoreCase("toCounselee")) {
			inputMap.put("subject", "Meet Expert session");
			valueMap.put("#COUNSELER_NAME#", toUser.getName());
			valueMap.put("#COUNSELE_NAME#", fromUser.getName());
			valueMap.put("#DATE#", start.toString());
			inputMap.put("to", fromUser.getEmail());
			filename = "ToCounselee.txt";
			//	inputMap.put("text", "Hi "+fromUser.getName()+", You have requested a session from "+ toUser.getName() +" on Meet Expert for "+proposedStartTime+". -Meet Expert.");
		} else if(event.equalsIgnoreCase("signUp")) {
			inputMap.put("subject", "Welcome to Meet Expert");
			inputMap.put("to", toUser.getEmail());
			valueMap.put("#USER_NAME#", toUser.getName());

			filename = "WelcomeMessage.txt";

			//	inputMap.put("text", "Hi "+toUser.getName()+", Thanks for joining Meet Expert. Enhance your skills. -Meet Expert.");
		} else if(event.equalsIgnoreCase("toCounselerReminder")) {
			inputMap.put("subject", "Meet Expert session");		
			inputMap.put("to", toUser.getEmail());
			valueMap.put("#COUNSELER_NAME#", toUser.getName());
			valueMap.put("#COUNSELE_NAME#", fromUser.getName());
			valueMap.put("#DATE#", start.toString());
			valueMap.put("#CUSTOM_MESSAGE#", " ");

			filename = "ToCounselerReminder.txt";
			//inputMap.put("text", "Hi "+toUser.getName()+", You have a new session request from "+ fromUser.getName() +" on Meet Expert for "+proposedStartTime+". -Meet Expert.");
		} else if(event.equalsIgnoreCase("toCounseleeReminder")) {
			inputMap.put("subject", "Meet Expert session");
			valueMap.put("#COUNSELER_NAME#", toUser.getName());
			valueMap.put("#COUNSELE_NAME#", fromUser.getName());
			valueMap.put("#DATE#", start.toString());
			inputMap.put("to", fromUser.getEmail());
			filename = "ToCounseleeReminder.txt";
			//	inputMap.put("text", "Hi "+fromUser.getName()+", You have requested a session from "+ toUser.getName() +" on Meet Expert for "+proposedStartTime+". -Meet Expert.");
		}


		StringBuffer sb = new StringBuffer();
		try {
			DataInputStream dis = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream(filename));
			String str = "";
			while (str != null) {
				str = dis.readLine();
				if (str != null) {
					sb.append(str);
					sb.append("\n");
				}
			}
		} catch (Exception ce) {
			ce.printStackTrace();
		}

		String emailMessage = sb.toString();

		Iterator<String> iterator = valueMap.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			String value = (String) valueMap.get(key);
			if (value == null) {
				continue;
			}
			if (value.contains("$")) {
				value = value.replaceAll("\\$", "\\\\\\$");
			}
			emailMessage = emailMessage.replaceAll(key, value);			
		}

		inputMap.put("text",emailMessage);


		Map<String, String> response  = MailUtil.sendMail(inputMap);

		System.out.println("for event: "+ event+" response :"+response.toString());


		return response;

	}


	@Transactional
	public Session stopSession(Long counselEventId) {
		Session counsellingEvent = sessionDAO.findById(counselEventId, false);
		if(counsellingEvent != null){
			counsellingEvent.setSessionStatus(SessionStatus.COMPLETED);
		}
		return counsellingEvent;
	}



	@Transactional
	public String updateSession(Long invitationId, Long userId, InvitationStatus invitationStatus) {
		Invitation invitation = invitationDAO.findById(invitationId, false);

		String statusStr = "success";
		if(invitation != null && invitation.getInvitee().getId().equals(userId) && 
				invitationStatus != invitation.getInvitationStatus()){

			invitation.setInvitationStatus(invitationStatus);
			invitationDAO.merge(invitation);


			if(invitation.getSession().getExpertInvitation().getId() == invitationId) {
				for(Viewer viewer: invitation.getSession().getViewers()) {

					MessageDTO messageDTO = new MessageDTO();
					if(viewer.getUser() == null) {
						//send Email
					} else {
						messageDTO.setToUser(GenericUtils.getUserDTO(viewer.getUser()));
						messageDTO.setFromUser(GenericUtils.getUserDTO(invitation.getInvitee()));

						messageDTO.setMessageType(MessageType.INFO);
						messageDTO.setMessageStatus(MessageStatus.CREATED);

						Category category = invitation.getSession().getCategory();
						Message updateMsg = new Message();
						StringBuffer messageStr = new StringBuffer("Your session on "+category.getName()+" has been ");
						if(invitationStatus == InvitationStatus.ACCEPTED) {
							if(!invitation.getSession().isOneToMany()) {
								invitation.getSession().setSessionStatus(SessionStatus.ACCEPTED);
							}
							updateMsg.setMessageStatus(MessageStatus.ACCEPTED);
							messageDTO.setMessage(messageStr.append("Accepted").toString());
						} else if(invitationStatus == InvitationStatus.REJECTED) {
							if(!invitation.getSession().isOneToMany()) {
								invitation.getSession().setSessionStatus(SessionStatus.CANCELLED);
							}
							updateMsg.setMessageStatus(MessageStatus.REJECTED);
							messageDTO.setMessage(messageStr.append("Rejected").toString());
						}
						createOrUpdateMessage(messageDTO);
						messageDAO.makePersistent(updateMsg);
					}
				}
			}

		}else{
			statusStr = "unauthorized";
		}
		return statusStr;
	}


	@Transactional
	public String updateExpertTalk(Long sessionId, Long userId, InvitationStatus invitationStatus) {

		String statusStr = "success";
		Session session = sessionDAO.findById(sessionId, false);
		Viewer viewer = new Viewer();
		User user = userDAO.findById(userId, false);
		viewer.setUser(user);
		viewer.setSession(session);
		viewerDAO.makePersistent(viewer);

		session.getViewers().add(viewer);
		sessionDAO.makePersistent(session);

		return statusStr;
	}


	@Transactional
	public String rateCounselEvent(Long counselEventId, Integer rating,
			String review, Long userId) {
		//CounsellingEvent counsellingEvent = counsellingEventDAO.findById(counselEventId, false);

		String statusStr = "success";
		/*
		if(counsellingEvent != null && counsellingEvent.getFrmUser().getId().equals(userId)){
			counsellingEvent.setRating(rating);
			counsellingEvent.setReview(review);
			counsellingEventDAO.merge(counsellingEvent);
		}else{
			statusStr = "unauthorized";
		}
		 */
		return statusStr;
	}


	@Transactional
	public void generateCoupon(CounselEventCreationRequest counselEventCreationRequest, User frmUser, int count) throws ParseException {
		Event event = new Event();
		event.setDescription(counselEventCreationRequest.getDescription());
		event.setOrganizer(frmUser);

		//counsellingEvent.setToUser(toUser);
		Date startDate;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

		startDate = sdf.parse(counselEventCreationRequest.getStartTime());
		event.setStart(startDate);

		Date endDate;
		endDate = sdf.parse(counselEventCreationRequest.getEndTime());
		event.setEnd(endDate);
		eventDAO.makePersistent(event);

		System.out.println("count is "+count);
		for(int i=0;i<count;i++){
			Coupon coupon = new Coupon();
			coupon.setStartTime(startDate);
			coupon.setEndTime(endDate);
			coupon.setUser(frmUser);
			coupon.setUsed(false);

			String couponCode = randomString(6);
			coupon.setCode(couponCode);
			coupon = couponDAO.makePersistent(coupon);

			EventSubscription es = new EventSubscription();
			es.setCoupon(coupon);
			es.setEvent(event);
			es.setUser(frmUser);
			eventSubscriptionDAO.makePersistent(es);
			System.out.println("generating coupon : "+couponCode);
		}



	}


	static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	static Random rnd = new Random();

	private String randomString( int len ) 
	{
		StringBuilder sb = new StringBuilder( len );
		for( int i = 0; i < len; i++ ) 
			sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
		return sb.toString();
	}

	@Transactional
	public List<CouponDTO> findCouponListPerUser(User user) {
		List<CouponDTO> couponDTOList = new ArrayList<CouponDTO>();
		List<Coupon> couponList = couponDAO.findByUser(user);
		for(Coupon coupon : couponList){
			CouponDTO couponDTO = new CouponDTO();
			couponDTO.setCode(coupon.getCode());
			couponDTO.setStartTime(coupon.getStartTime());
			couponDTO.setEndTime(coupon.getEndTime());
			couponDTO.setUsed(((coupon.isUsed())?"Used":"UnUsed"));

			couponDTOList.add(couponDTO);
		}
		return couponDTOList;
	}


	@Transactional
	public void createOrUpdateMessage(MessageDTO messageDTO) {
		Message message = null;
		if(messageDTO.getId() == null) {
			message = new Message();
		} else {
			message = messageDAO.findById(messageDTO.getId(), false);
		}
                if(messageDTO.getFromUser()!=null){
                    message.setFromId(messageDTO.getFromUser().getId());
                }else{
                       message.setInviterEmail(messageDTO.getFromEmail());
                }
                
		message.setToId(messageDTO.getToUser().getId());
		message.setMessageContent(messageDTO.getMessage());
		message.setMessageStatus(messageDTO.getMessageStatus());
		message.setMessageType(messageDTO.getMessageType());
		message.setScheduledDate(messageDTO.getScheduleDate());
		message.setPeriodOfTime(messageDTO.periodOfTime);

		if(messageDTO.getCounselRequestDTO() != null) {
			message.setSessionId(messageDTO.getCounselRequestDTO().getId());
		}
		if(messageDTO.getInvitationId() != null) {
			message.setInvitationId(messageDTO.getInvitationId());
		}
                
		messageDAO.makePersistent(message);
		messageDTO.setId(message.getId());
	}

	@Transactional
	public void updateMessageStatus(MessageDTO messageDTO) {
		Message message = messageDAO.findById(messageDTO.getId(), false);
		message.setMessageStatus(messageDTO.getMessageStatus());
		messageDAO.makePersistent(message);
	}

	@Transactional
	public List<MessageDTO> getUserMessages(User user , Date lastMessageTime) {
		List<MessageDTO> messageDTOs = new ArrayList<MessageDTO>();
		List<Message> messages = messageDAO.findByUser(user, lastMessageTime);
		if(messages != null) {
			for(Message message : messages) {
				MessageDTO messageDTO = new MessageDTO();
				messageDTO.setFromUser(GenericUtils.getUserDTO(userDAO.findById(message.getFromId(),false)));
				messageDTO.setMessage(message.getMessageContent());
				messageDTO.setMessageStatus(message.getMessageStatus());
				messageDTO.setMessageType(message.getMessageType());
				messageDTO.setPeriodOfTime(message.getPeriodOfTime());
				messageDTO.setToUser(GenericUtils.getUserDTO(userDAO.findById(message.getToId(), false)));
				messageDTO.setScheduleDate(message.getScheduledDate());
				messageDTO.setCreationTime(message.getCreationTime());
				messageDTO.setInvitationId(message.getInvitationId());
				CounselRequestDTO counselRequestDTO = null;
				if(message.getMessageType() == MessageType.SESSIONREQUEST) {
					counselRequestDTO = new CounselRequestDTO();
					Session counsellingEvent = sessionDAO.findById(message.getSessionId(), false);
					counselRequestDTO.setDescription(counsellingEvent.getHtmlDescription());
					counselRequestDTO.setEndTime(counsellingEvent.getEndTime());
					counselRequestDTO.setStartTime(counsellingEvent.getStartTime());
					counselRequestDTO.setId(counsellingEvent.getId());
					//counselRequestDTO.setFrmuserDTO(GenericUtils.getUserDTO(counsellingEvent.getFrmUser()));
					//counselRequestDTO.setToUserDTO(GenericUtils.getUserDTO(counsellingEvent.getToUser()));

				}
				messageDTO.setCounselRequestDTO(counselRequestDTO);
				messageDTO.setId(message.getId());
				messageDTOs.add(messageDTO);
			}

		}
		return messageDTOs;
	}

	@Transactional
	public boolean createOneToMany(OneToMany manyToOneRequest, User frmUser)
	{

		try {
			Date startDate;
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
			startDate = sdf.parse(manyToOneRequest.getStartTime());
			Date endDate;
			endDate = sdf.parse(manyToOneRequest.getEndTime());
			Session session = new Session();
			session.setCategory(categoryDAO.findByName(manyToOneRequest.getCategoryName()));
			session.setExpert(frmUser);
			session.setSessionStatus(SessionStatus.ACCEPTED);
			session.setStartTime(startDate);
			session.setEndTime(endDate);
			session.setOneToMany(true);
			session.setMaxSeats(Integer.valueOf(manyToOneRequest.getNumberOfSeats()));
			session.setTitle(manyToOneRequest.getDescription());
			session.setPrice(Integer.valueOf(manyToOneRequest.getPrice()));
			sessionDAO.makePersistent(session);
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return true;
	}

	@Transactional
	public EventSubscription subscribe(Long counselEventId, Long id) {
		User user = userDAO.findById(id, false);
		if(counselEventId == null) {
			return null;
		}
		Event event = eventDAO.findById(counselEventId, false);
		if (event == null) {
			return null;
		}
		if (new Date().after(event.getEnd())) {
			return null;
		}
		if(!event.hasMoreSlots()) {
			return null;
		}
		EventSubscription eventSubscription = new EventSubscription();
		eventSubscription.setEvent(event);
		eventSubscription.setUser(user);
		return null;
	}

	@Transactional
	public boolean checkForConflits(Date startDate, Date endDate, User user) {
		//return eventDAO.isConflicted(user.getId(), startDate, endDate) &&
		//		counsellingEventDAO.isConflicted(user.getId(), startDate, endDate);
		return true;
		//Have to check one to many as well;
	}

	@Transactional
	public boolean checkAvaliability(Date startDate, Date endDate, User expert) {
		Calendar startCal=Calendar.getInstance();
		Availability availability=availabilityDAO.findByUser(expert.getId());

		if(availability==null){
			return true;
		}

		startCal.setTime(startDate);
		String checkString=null;
		switch(startCal.get(Calendar.DAY_OF_WEEK)){
		case Calendar.SUNDAY: checkString=availability.getSunday(); break;
		case Calendar.MONDAY: checkString=availability.getMonday(); break;
		case Calendar.TUESDAY: checkString=availability.getTuesday(); break;
		case Calendar.WEDNESDAY: checkString=availability.getWednesday(); break;
		case Calendar.THURSDAY: checkString=availability.getThursday(); break;
		case Calendar.FRIDAY: checkString=availability.getFriday(); break;
		case Calendar.SATURDAY: checkString=availability.getSaturday(); break;
		}


		Calendar endCal=Calendar.getInstance();
		endCal.setTime(endDate);

		if(checkString==null){
			return false;
		}

		for(String avilTime:checkString.split(",")){
			String[] temp=avilTime.split("-");
			String startTime=temp[0];
			String endTime=temp[1];

			int stHr=Integer.parseInt(startTime.split(":")[0]);
			int stMin=Integer.parseInt(startTime.split(":")[1]);
			int endHr=Integer.parseInt(endTime.split(":")[0]);
			int endMin=Integer.parseInt(endTime.split(":")[1]);

			if(startCal.get(Calendar.HOUR_OF_DAY)>=stHr 
					&& startCal.get(Calendar.MINUTE)>=stMin
					&& endCal.get(Calendar.HOUR_OF_DAY)<=endHr
					&& endCal.get(Calendar.MINUTE)<=endMin){
				return true;
			}

		}

		return false;
	}

	
	@Transactional
	public CalendarDTO getCalander(Date fromDate, Date toDate, User requestedUser,long userId) {
		List<Session> sessions = sessionDAO.getCalnader(userId, fromDate, toDate);
                List<CalEvent> list = new LinkedList<CalEvent>();
		for (Session se:sessions) {
			CalEvent ca;
			if(se.isOneToMany() || userId==requestedUser.getId() ||
                                se.getExpert().getId()==requestedUser.getId() ){
				ca=new CalEvent(se.getTitle(),se.getHtmlDescription(), null,se.getId(),(short)0, se.getStartTime(),se.getEndTime(), Boolean.FALSE);
			}else{
				ca=new CalEvent("BUSY","NA", null,se.getId(),(short)0, se.getStartTime(),se.getEndTime(), Boolean.FALSE);
			}
			list.add(ca);
		}
                CalendarDTO cdto= new CalendarDTO((short)0, "Success");
                cdto.setStartDate(toDate);
                cdto.setEndDate(fromDate);
                cdto.setEvents(list);
		return cdto;
	}
	 
	@SuppressWarnings("unused")
	private List<CalEvent> getAvaliability(Date date,User user){
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		Availability availability=availabilityDAO.findByUser(user.getId());
		String checkString=null;
		switch(cal.get(Calendar.DAY_OF_WEEK)){
		case Calendar.SUNDAY: checkString=availability.getSunday(); break;
		case Calendar.MONDAY: checkString=availability.getMonday(); break;
		case Calendar.TUESDAY: checkString=availability.getTuesday(); break;
		case Calendar.WEDNESDAY: checkString=availability.getWednesday(); break;
		case Calendar.THURSDAY: checkString=availability.getThursday(); break;
		case Calendar.FRIDAY: checkString=availability.getFriday(); break;
		case Calendar.SATURDAY: checkString=availability.getSaturday(); break;
		}

		if(checkString==null){//As the user is not avaliable return all day evenet
			CalEvent calEvent= new CalEvent("Not avaliable", "", null,null, null, date, date, Boolean.TRUE);
			List<CalEvent> list = new LinkedList<CalEvent>();
			list.add(calEvent);
			return list;
		}
		List<CalEvent> list = new LinkedList<CalEvent>();

		Calendar start = Calendar.getInstance();
		start.setTime(date);
		start.set(Calendar.HOUR, 0);
		start.set(Calendar.MINUTE, 0);
		start.set(Calendar.SECOND, 0);
		for(String avilTime:checkString.split(",")){
			String[] temp=avilTime.split("-");
			String avlStartTime=temp[0];
			String avlEndTime=temp[1];

		}
		return null;
	}

	@Transactional
	public FileUploadResponseDTO uploadFileToAmazon(NexusFileUpload nexusFileUpload,
			Long counselEventId) {

		FileUploadResponseDTO fileUploadResponseDTO = new FileUploadResponseDTO();

		try{
			Date date = new Date();
			String fileUrl = FileUploadUtils.uploadFileToAmazon(nexusFileUpload, String.valueOf(counselEventId), date.toString());
			String fileName = nexusFileUpload.getMultiPartFile().getOriginalFilename();
			fileUploadResponseDTO.setFileName(fileName);
			fileUploadResponseDTO.setFileUrl(fileUrl);
		}catch(Exception exception){
			exception.printStackTrace();
			throw new RuntimeException(exception);
		}

		return fileUploadResponseDTO;
	}

	
	
	@Transactional
	public String addFavoriteExpert(Long expertId, User user) {
		String status = "failed!";
		User favUser = userDAO.findById(expertId, false);
		Set<User> favExperts = user.getMyFavoriteUsers();
		if(favExperts == null) {
			favExperts = new HashSet<User>();
		}
		if(!favExperts.contains(favUser)) {
			favExperts.add(favUser);
			user.setMyFavoriteUsers(favExperts);
			userDAO.makePersistent(user);
		}
		status = "success";
		return status;
	}
	
	@Transactional
	public List<UserDTO> getFavoriteExperts(User user) {
		Set<User> userList = user.getMyFavoriteUsers();
		return GenericUtils.getUserDTO(userList);
	}
	
	@Transactional
	public String deleteFavoriteExpert(Long favUserId, User user) {
		String status = "failed!";
		User favUser = userDAO.findById(favUserId, false);
		Set<User> favExperts = user.getMyFavoriteUsers();
		favExperts.remove(favUser);
		user.setMyFavoriteUsers(favExperts);
		status = "success";
		return status;
	}

}
